package cn.bdqn.service.impl;

import cn.bdqn.mapper.ItemKillMapper;
import cn.bdqn.mapper.ItemKillSuccessMapper;
import cn.bdqn.mapper.MethodLockMapper;
import cn.bdqn.pojo.ItemKill;
import cn.bdqn.pojo.ItemKillSuccess;
import cn.bdqn.pojo.MethodLock;
import cn.bdqn.service.ItemKillService;
import cn.bdqn.service.RabbitMQProviderService;
import cn.bdqn.utils.SnowFlake;
import cn.bdqn.vo.EmailDto;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.SQLException;
import java.util.List;

@Service
public class ItemKillServiceImpl implements ItemKillService {

    private static final Logger log = LoggerFactory.getLogger(ItemKillServiceImpl.class);

    @Resource
    private ItemKillMapper itemKillMapper;
    @Resource
    private ItemKillSuccessMapper itemKillSuccessMapper;
    @Resource
    private MethodLockMapper methodLockMapper;
    @Resource
    private SqlSession sqlSession;
    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    @Resource
    private RabbitMQProviderService rabbitMQProviderService;

    /**
     * 查询秒杀的商品列表
     */
    @Override
    public List<ItemKill> findAllItemKill() {
        List<ItemKill> itemKills = itemKillMapper.findAllItemKill();
        return itemKills;
    }

    /**
     * 根据商品ID查询商品的详情
     */
    @Override
    public ItemKill findByItemId(Integer itemId) {
        ItemKill itemKill = itemKillMapper.findByItemId(itemId);
        if (itemKill != null) {
            return itemKill;
        } else {
            throw new RuntimeException("该商品ID不存在");
        }
    }

    /**
     * 扣减可秒杀的商品数量，使用synchronized实现，22  17   16
     */
    @Override
    @Transactional
    public synchronized int updateById(Integer id, Integer userId) {
        //验证该用户是否以及秒杀过该商品
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("user_id", userId);
        queryWrapper.eq("kill_id", id);
        Integer count = itemKillSuccessMapper.selectCount(queryWrapper);
        if (count <= 0) {
            //先验证库存是否充足
            ItemKill itemKill = itemKillMapper.selectById(id);
            if (itemKill.getTotal() > 0) {
                //库存扣减
                int row = itemKillMapper.updateById(id);
                //验证库存扣减是否成功
                if (row > 0) {
                    //生成秒杀成功的订单
                    row = createKillOrder(itemKill, userId);
                    return row;
                } else {
                    throw new RuntimeException("秒杀失败，库存不足");
                }
            } else {
                throw new RuntimeException("秒杀失败，库存不足");
            }
        } else {
            throw new RuntimeException("秒杀失败，您已经秒杀过该商品");
        }
    }

    /**
     * 扣减可秒杀的商品数量，借助第三表是否存在对于记录（唯一索引） 13   12
     */
    @Override
    @Transactional
    public int updateById1(Integer id, Integer userId) {
        try {
            MethodLock methodLock = new MethodLock("1","updateById1",null);
            int row = methodLockMapper.insert(methodLock);
            if (row > 0) {
                //验证该用户是否以及秒杀过该商品
                QueryWrapper queryWrapper = new QueryWrapper();
                queryWrapper.eq("user_id", userId);
                queryWrapper.eq("kill_id", id);
                Integer count = itemKillSuccessMapper.selectCount(queryWrapper);
                if (count <= 0) {
                    //先验证库存是否充足
                    ItemKill itemKill = itemKillMapper.selectById(id);
                    if (itemKill.getTotal() > 0) {
                        //库存扣减
                        row = itemKillMapper.updateById(id);
                        //验证库存扣减是否成功
                        if (row > 0) {
                            //生成秒杀成功的订单
                            row = createKillOrder(itemKill, userId);
                            return row;
                        } else {
                            throw new RuntimeException("秒杀失败，库存不足");
                        }
                    } else {
                        throw new RuntimeException("秒杀失败，库存不足");
                    }
                } else {
                    throw new RuntimeException("秒杀失败，您已经秒杀过该商品");
                }
            } else {
                throw new RuntimeException("没有获取到锁，秒杀失败");
            }
        } finally {
            //删除记录
            methodLockMapper.deleteById("1");
        }
    }

    /**
     * 扣减可秒杀的商品数量，使用mysql的悲观锁（for update），行锁和表锁，尽量不要使用表锁   22    21
     */
    @Override
    @Transactional
    public int updateById2(Integer id, Integer userId) throws SQLException {
        //关闭自动提交
        sqlSession.getConnection().setAutoCommit(false);
        try {
            ItemKill itemKill1 = itemKillMapper.findLock(id);
            if (itemKill1 != null) {
                //验证该用户是否以及秒杀过该商品
                QueryWrapper queryWrapper = new QueryWrapper();
                queryWrapper.eq("user_id", userId);
                queryWrapper.eq("kill_id", id);
                Integer count = itemKillSuccessMapper.selectCount(queryWrapper);
                if (count <= 0) {
                    //先验证库存是否充足
                    ItemKill itemKill = itemKillMapper.selectById(id);
                    if (itemKill.getTotal() > 0) {
                        //库存扣减
                        int row = itemKillMapper.updateById(id);
                        //验证库存扣减是否成功
                        if (row > 0) {
                            //生成秒杀成功的订单
                            row = createKillOrder(itemKill, userId);
                            return row;
                        } else {
                            throw new RuntimeException("秒杀失败，库存不足");
                        }
                    } else {
                        throw new RuntimeException("秒杀失败，库存不足");
                    }
                } else {
                    throw new RuntimeException("秒杀失败，您已经秒杀过该商品");
                }
            } else {
                throw new RuntimeException("没有获取到锁，秒杀失败");
            }
        }finally {
            //释放锁
            sqlSession.getConnection().commit();
        }
    }

    /**
     * 扣减可秒杀的商品数量，使用mysql的乐观锁
     */

    /**
     * 扣减可秒杀的商品数量，使用redis，基于setnx实现分布式锁  6   5   4
     */
    @Override
    @Transactional
    public int updateById4(Integer id, Integer userId) throws SQLException {
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        //key
        String key = new StringBuilder().append(id).append(userId).append("-redisLock").toString();
        //value
        Object value = new SnowFlake(1, 1, 1).nextId();
        try {
            //setnx在redisTemplate中对的API就是setIfAbsent()
            Boolean aBoolean = operations.setIfAbsent(key, value);
            if (aBoolean) {
                //验证该用户是否以及秒杀过该商品
                QueryWrapper queryWrapper = new QueryWrapper();
                queryWrapper.eq("user_id", userId);
                queryWrapper.eq("kill_id", id);
                Integer count = itemKillSuccessMapper.selectCount(queryWrapper);
                if (count <= 0) {
                    //先验证库存是否充足
                    ItemKill itemKill = itemKillMapper.selectById(id);
                    if (itemKill.getTotal() > 0) {
                        //库存扣减
                        int row = itemKillMapper.updateById(id);
                        //验证库存扣减是否成功
                        if (row > 0) {
                            //生成秒杀成功的订单
                            row = createKillOrder(itemKill, userId);
                            return row;
                        } else {
                            throw new RuntimeException("秒杀失败，库存不足");
                        }
                    } else {
                        throw new RuntimeException("秒杀失败，库存不足");
                    }
                } else {
                    throw new RuntimeException("秒杀失败，您已经秒杀过该商品");
                }
            } else {
                throw new RuntimeException("没有获取到锁，秒杀失败");
            }
        }finally {
        }
    }

    /**
     * 扣减可秒杀的商品数量，使用redisson实现分布式锁
     */

    /**
     * 创建秒杀订单
     */
    @Transactional
    public int createKillOrder(ItemKill itemKill, Integer userId) {
        ItemKillSuccess itemKillSuccess = new ItemKillSuccess();
        //雪花算法，生成订单编号
        SnowFlake snowFlake = new SnowFlake(1, 1, 1);
        itemKillSuccess.setCode(String.valueOf(snowFlake.nextId()));
        itemKillSuccess.setKillId(itemKill.getId());
        itemKillSuccess.setItemId(itemKill.getItemId());
        itemKillSuccess.setUserId(userId);
        int row = itemKillSuccessMapper.insert(itemKillSuccess);
        if (row > 0) {
            log.info("秒杀订单创建成功");
            //发送一个消息给mq的邮件队列，告诉用户已经秒杀成功！
            EmailDto emailDto = new EmailDto();
            emailDto.setEmail("3079471479@qq.com");
            emailDto.setCode(itemKillSuccess.getCode());
            emailDto.setItemName("卫龙辣条");
            emailDto.setStatus(0);
            emailDto.setPayAddress("https://www.baidu.com");
            rabbitMQProviderService.send(emailDto);

            //把生成的订单号发送到死信队列
            rabbitMQProviderService.deadSend(itemKillSuccess.getCode());
            return row;
        } else {
            throw new RuntimeException("秒杀失败，订单创建失败");
        }
    }
}
